package top.xunfong.knowledge.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import top.xunfong.common.core.exception.ServiceException;
import top.xunfong.knowledge.dto.*;
import top.xunfong.knowledge.service.MultipartService;
import top.xunfong.knowledge.vo.*;
import top.xunfong.redis.util.RedisUtil;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

/**
 * 分片上传服务实现类
 */
@Service
public class MultipartServiceImpl implements MultipartService {

    private static final String REDIS_KEY_PREFIX = "upload:";

    private static final String PART = "part:";

    @Value("${file.baseUrl:D:/upload/}")
    private String UPLOAD_DIR;

    @Override
    public InitiateMultipartUploadVO initiateMultipartUpload(InitiateMultipartUploadDTO dto) {
        String uploadId = UUID.randomUUID().toString();
        RedisUtil.hPut(REDIS_KEY_PREFIX + uploadId, "fileName", dto.getFileName());
        RedisUtil.hPut(REDIS_KEY_PREFIX + uploadId, "fileMd5", dto.getFileMd5());
        InitiateMultipartUploadVO vo = new InitiateMultipartUploadVO();
        vo.setUploadId(uploadId);
        return vo;
    }

    @Override
    public UploadPartVO uploadPart(UploadPartDTO dto) {
        try {
            String uploadDir = UPLOAD_DIR + File.separator + dto.getUploadId();
            Files.createDirectories(Paths.get(uploadDir));
            String partPath = uploadDir + File.separator + dto.getPartNumber();

            FileUtil.writeBytes(dto.getFile().getBytes(), partPath);
            RedisUtil.hPut(REDIS_KEY_PREFIX + dto.getUploadId(), "part:" + dto.getPartNumber(), partPath);
            return null;
        } catch (IOException e) {
            throw new ServiceException("分片上传失败");
        }
    }

    @Override
    public CompleteMultipartUploadVO completeMultipartUpload(CompleteMultipartUploadDTO dto) {
        try {
            String uploadDir = UPLOAD_DIR + File.separator + dto.getUploadId();
            String fileName = RedisUtil.hGet(REDIS_KEY_PREFIX + dto.getUploadId(), "fileName");
            Path mergedFilePath = Paths.get(UPLOAD_DIR, fileName);
            Files.createFile(mergedFilePath);
            // 获取所有的key
            Set<String> keys = RedisUtil.hGetAllKey(REDIS_KEY_PREFIX + dto.getUploadId());
            // 获取所有的partNumber
            List<Integer> partNumbers = keys.stream().filter(key -> key.startsWith(PART))
                    .map(key -> Integer.parseInt(key.substring(PART.length())))
                    .sorted()
                    .toList();

            for (Integer partNumber : partNumbers) {
                String partPath = (String) RedisUtil.hGet(REDIS_KEY_PREFIX + dto.getUploadId(), "part:" + partNumber);
                Files.write(mergedFilePath, Files.readAllBytes(Paths.get(partPath)), StandardOpenOption.APPEND);
            }

            RedisUtil.delete(REDIS_KEY_PREFIX + dto.getUploadId());
            return new CompleteMultipartUploadVO();
        } catch (IOException e) {
            throw new RuntimeException("文件合并失败", e);
        }
    }

    @Override
    public AbortMultipartUploadVO abortMultipartUpload(AbortMultipartUploadDTO dto) {
        try {
            String uploadDir = UPLOAD_DIR + File.separator + dto.getUploadId();
            Files.walk(Paths.get(uploadDir))
                    .sorted(Comparator.reverseOrder())
                    .map(Path::toFile)
                    .forEach(File::delete);
            RedisUtil.delete(REDIS_KEY_PREFIX + dto.getUploadId());
            return null;
        } catch (IOException e) {
            throw new RuntimeException("取消上传失败", e);
        }

    }
}